<template>
	<div ref="jsonEditorRef" class="jsonEditor" :style="{ height: defaultHeight }"></div>
</template>

<script lang="ts" setup>
import { onMounted, onUnmounted, reactive, ref, watch, computed } from "vue";
import JSONEditor from "jsoneditor";
import "jsoneditor/dist/jsoneditor.min.css";
import { nsToPxPct } from "@/util";
import type { JSONEditorOptions, JSONEditorMode } from "jsoneditor";

const props = withDefaults(
	defineProps<{
		options?: JSONEditorOptions;
		name?: string;
		mode?: JSONEditorMode;
		modelValue?: object;
		value?: object | string;
		collapse?: boolean;
		height?: number | string;
	}>(),
	{
		options: () => ({}),
		name: "",
		mode: "code",
		modelValue: () => ({}),
		value: () => ({}),
		collapse: false,
		height: "100%",
	},
);

const emit = defineEmits<{
	(event: "update:modelValue", value: any): void;
	(event: "change", value: any): void;
}>();

const jsonEditorRef = ref();
let editor: JSONEditor;

const options = reactive<JSONEditorOptions>({
	name: props.name,
	mode: props.mode,
	modes: ["code", "form", "text", "tree", "view", "preview"],
	onChange: function () {
		emit("update:modelValue", getValue());
		emit("change", getValue());
	},
});

const defaultHeight = computed(() => nsToPxPct(props.height));

watch(
	() => props.collapse,
	() => {
		isCollapse();
	},
);

onMounted(() => {
	init();
});

onUnmounted(() => {
	destroy();
});

function setValue(val: object) {
	editor.set(val);
}

function getValue() {
	return editor.get();
}

// 展开或收起
function isCollapse() {
	if (["tree", "view", "form"].includes(editor.getMode())) {
		props.collapse ? editor.collapseAll() : editor.expandAll();
	}
}

function init() {
	// 自定义配置
	const userOptions: JSONEditorOptions = JSON.parse(JSON.stringify(props.options));
	delete userOptions.mode;
	delete userOptions.modes;
	const mergeOptions = Object.assign(options, userOptions);
	// 默认值

	editor = new JSONEditor(
		jsonEditorRef.value,
		mergeOptions,
		props.modelValue ? props.modelValue : props.value,
	);
	isCollapse();
}

function destroy() {
	editor.destroy();
}

defineExpose({
	setValue,
	getValue,
	init,
});
</script>

<style lang="scss" scoped>
.jsonEditor {
	width: 100%;
}
</style>
